/*
 * uni-app 请求封装
 * - 基于 uni.request
 * - 支持：get/post/put/delete、uploadFile
 * - 功能：baseURL、超时、token 注入、统一错误处理、可配置是否显示错误 toast
 * 用法示例：
 * import api, { setBaseURL, setToken } from '@/utils/api'
 * setBaseURL('https://api.example.com')
 * setToken('your-token')
 * api.get('/path', { id: 1 }).then(res => {}).catch(err => {})
 */

const defaultConfig = {
	baseURL: '',
	timeout: 15000, // ms
	header: {
		'content-type': 'application/json'
	},
	showErrorToast: true // 出错时是否显示 uni.showToast
}

let runtimeConfig = Object.assign({}, defaultConfig)

// 可注入的回调：当收到 401/需要登录时触发
let onAuthFail = null

function setBaseURL(url) {
	url && runtimeConfig.baseURL = url
}

function setTimeout(ms) {
	runtimeConfig.timeout = ms
}

function setShowErrorToast(flag) {
	runtimeConfig.showErrorToast = !!flag
}

function setAuthFailHandler(fn) {
	onAuthFail = typeof fn === 'function' ? fn : null
}

function getToken() {
	try {
		return uni.getStorageSync('token') || ''
	} catch (e) {
		return ''
	}
}

function setToken(token) {
	try {
		if (token === null || token === undefined || token === '') {
			uni.removeStorageSync('token')
		} else {
			uni.setStorageSync('token', token)
		}
	} catch (e) {
		// ignore
	}
}

function buildURL(url) {
	if (!url) return ''
	if (/^https?:\/\//.test(url)) return url
	const base = runtimeConfig.baseURL || ''
	// ensure slashes correct
	if (!base) return url
	return (base.replace(/\/$/, '') + '/' + url.replace(/^\//, ''))
}

function request(options = {}) {
	return new Promise((resolve, reject) => {
		if (!options || !options.url) {
			const err = { message: 'request: url is required', options }
			runtimeConfig.showErrorToast && uni.showToast({ title: err.message, icon: 'none' })
			return reject(err)
		}

		const method = (options.method || 'GET').toUpperCase()
		const url = buildURL(options.url)
		const header = Object.assign({}, runtimeConfig.header, options.header || {})

		const token = getToken()
		if (token) {
			// 常见后端使用 Authorization 或 token 字段，根据项目需要调整
			header['Authorization'] = header['Authorization'] || `Bearer ${token}`
		}

		let timedOut = false
		const timer = setTimeout(() => {
			timedOut = true
			const err = { message: '请求超时', url, options }
			runtimeConfig.showErrorToast && uni.showToast({ title: err.message, icon: 'none' })
			reject(err)
		}, runtimeConfig.timeout)

		uni.request({
			url,
			data: options.data || options.params || {},
			header,
			method,
			timeout: runtimeConfig.timeout,
			success(res) {
				if (timedOut) return
				clearTimeout(timer)

				// HTTP 层状态码判断
				const statusCode = res.statusCode || 0
				const data = res.data

				if (statusCode >= 200 && statusCode < 300) {
					// 约定：后端正常响应体为 { code: 0, data: ..., message: '' } 或直接为数据
					if (data && (data.code !== undefined)) {
						if (data.code === 0) {
							resolve(data)
						} else if (data.code === 401) {
							// 鉴权失败
							runtimeConfig.showErrorToast && uni.showToast({ title: data.message || '需要登录', icon: 'none' })
							if (typeof onAuthFail === 'function') onAuthFail(data)
							reject(data)
						} else {
							// 非 0 的业务错误
							runtimeConfig.showErrorToast && uni.showToast({ title: data.message || '服务器返回错误', icon: 'none' })
							reject(data)
						}
					} else {
						// 如果不是标准结构，直接返回 data
						resolve({ code: 0, data, message: '' })
					}
				} else if (statusCode === 401) {
					// HTTP 401
					runtimeConfig.showErrorToast && uni.showToast({ title: '登录已失效，请重新登录', icon: 'none' })
					if (typeof onAuthFail === 'function') onAuthFail({ statusCode })
					reject({ statusCode, message: 'Unauthorized' })
				} else {
					const message = (data && data.message) || `HTTP ${statusCode}`
					runtimeConfig.showErrorToast && uni.showToast({ title: message, icon: 'none' })
					reject({ statusCode, message, data })
				}
			},
			fail(err) {
				if (timedOut) return
				clearTimeout(timer)
				runtimeConfig.showErrorToast && uni.showToast({ title: err.errMsg || '网络错误', icon: 'none' })
				reject(err)
			}
		})
	})
}

// 简便方法
function get(url, params = {}, options = {}) {
	return request(Object.assign({}, options, { url, method: 'GET', params }))
}

function post(url, data = {}, options = {}) {
	return request(Object.assign({}, options, { url, method: 'POST', data }))
}

function put(url, data = {}, options = {}) {
	return request(Object.assign({}, options, { url, method: 'PUT', data }))
}

function del(url, data = {}, options = {}) {
	return request(Object.assign({}, options, { url, method: 'DELETE', data }))
}

function uploadFile({ url, filePath, name = 'file', formData = {}, header = {} } = {}) {
	return new Promise((resolve, reject) => {
		if (!url || !filePath) {
			const err = { message: 'uploadFile: url and filePath are required' }
			runtimeConfig.showErrorToast && uni.showToast({ title: err.message, icon: 'none' })
			return reject(err)
		}

		const fullUrl = buildURL(url)
		const token = getToken()
		if (token) {
			header['Authorization'] = header['Authorization'] || `Bearer ${token}`
		}

		uni.uploadFile({
			url: fullUrl,
			filePath,
			name,
			formData,
			header: Object.assign({}, runtimeConfig.header, header),
			success(res) {
				// 上传返回的是字符串，需要尝试 JSON.parse
				let data = res.data
				try { data = JSON.parse(res.data) } catch (e) { /* ignore */ }
				// 与 request 保持一致的返回规范
				if (data && data.code !== undefined) {
					if (data.code === 0) resolve(data)
					else reject(data)
				} else {
					resolve({ code: 0, data, message: '' })
				}
			},
			fail(err) {
				runtimeConfig.showErrorToast && uni.showToast({ title: err.errMsg || '上传失败', icon: 'none' })
				reject(err)
			}
		})
	})
}

const api = {
	request,
	get,
	post,
	put,
	delete: del,
	uploadFile,
	// config helpers
	setBaseURL,
	setTimeout,
	setShowErrorToast,
	setToken,
	getToken,
	setAuthFailHandler,
	_config: runtimeConfig
}

export default api
export { api, setBaseURL, setTimeout, setShowErrorToast, setToken, getToken, setAuthFailHandler }

